package com.shr25.robot.qq.mirai;

import cn.hutool.core.io.FileUtil;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
import net.mamoe.mirai.Bot;
import net.mamoe.mirai.auth.QRCodeLoginListener;
import net.mamoe.mirai.utils.DeviceVerificationRequests;
import net.mamoe.mirai.utils.DeviceVerificationResult;
import net.mamoe.mirai.utils.LoginSolver;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;


public class WebLoginSolver extends LoginSolver {

    Logger log = LoggerFactory.getLogger(getClass());
    /** bot的号码 */
    Long qq;

    /** 登录需要处理的事件类型 0.登录成功  1.图片验证码  2. 滑动验证码 3.理不安全设备验证 4.短信 5.扫码二维码 9.异常*/
    Integer type;

    /** 需要验证的机器人 */
    Bot bot;

    /** 图片验证码 */
    byte[] imgData;

    /** 需要处理的url */
    String url;

    /** 图片验证码的值 */
    String code;

    /** 滑动模块的ticket */
    String ticket;

    /** 是否发送短信验证码 */
    Boolean isSms;

    /** 短信验证码 */
    String smsCode;

    public Long getQq() {
        return qq;
    }

    public void setQq(Long qq) {
        this.qq = qq;
    }

    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }

    public Bot getBot() {
        return bot;
    }

    public void setBot(Bot bot) {
        this.bot = bot;
    }

    public byte[] getImgData() {
        return imgData;
    }

    public void setImgData(byte[] imgData) {
        this.imgData = imgData;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getTicket() {
        return ticket;
    }

    public void setTicket(String ticket) {
        this.ticket = ticket;
    }

    public Boolean getIsSms() {
        return isSms;
    }

    public void setIsSms(Boolean isSms) {
        this.isSms = isSms;
    }

    public String getSmsCode() {
        return smsCode;
    }

    public void setSmsCode(String smsCode) {
        this.smsCode = smsCode;
    }

    @Override
    public boolean isSliderCaptchaSupported() {
        return true;
    }
    /**
     * 处理图片验证码.
     * 返回 null 以表示无法处理验证码, 将会刷新验证码或重试登录. 抛出一个 LoginFailedException 以正常地终止登录, 抛出任意其他 Exception 将视为异常终止
     * @param bot
     * @param bytes
     * @param continuation
     * @return
     */
    @Nullable
    @Override
    public Object onSolvePicCaptcha(@NotNull Bot bot, @NotNull byte[] bytes, @NotNull Continuation<? super String> continuation) {
        this.qq = bot.getId();
        this.type = 1;
        this.bot = bot;
        this.imgData = bytes;
        log.info("[PicCaptcha] 需要图片验证码登录, 验证码为 4 字母" );
        log.info("[PicCaptcha] Picture captcha required. Captcha consists of 4 letters.");
        Long currTime = System.currentTimeMillis();
        //最大等待检测5分钟，没有值返回
        for (;System.currentTimeMillis()-currTime > 300000;) {
            if(code != null){
                break;
            }
        }
        return code;
    }

    /**
     * 处理滑动验证码.
     *
     * 返回 `null` 以表示无法处理验证码, 将会刷新验证码或重试登录.
     * 抛出一个 [LoginFailedException] 以正常地终止登录, 抛出任意其他 [Exception] 将视为异常终止
     *
     * @return 验证码解决成功后获得的 ticket.
     */
    @Nullable
    @Override
    public Object onSolveSliderCaptcha(@NotNull Bot bot, @NotNull String url, @NotNull Continuation<? super String> continuation) {
        this.qq = bot.getId();
        this.type = 2;
        this.bot = bot;
        this.url = url;
        log.info("[SliderCaptcha] 需要滑动验证码, 请按照以下链接的步骤完成滑动验证码, 然后输入获取到的 ticket");
        log.info("[SliderCaptcha] Slider captcha required. Please solve the captcha with following link. Type ticket here after completion.");
        log.info("[SliderCaptcha] @see https://github.com/project-mirai/mirai-login-solver-selenium");
        log.info("[SliderCaptcha] @see https://docs.mirai.mamoe.net/mirai-login-solver-selenium/");
        log.info("[SliderCaptcha] 或者输入 TxCaptchaHelper 来使用 TxCaptchaHelper 完成滑动验证码");
        log.info("[SliderCaptcha] Or type `TxCaptchaHelper` to resolve slider captcha with TxCaptchaHelper.apk");
        log.info("[SliderCaptcha] Captcha link: {}", url);
        Long currTime = System.currentTimeMillis();
        //最大等待检测1分钟，没有值返回
        for (;System.currentTimeMillis()-currTime <= 120000;) {
            if(ticket != null){
                log.info("com.shr25.robot.qq.mirai.WebLoginSolver.onSolveSliderCaptcha：设置ticket: {}", ticket);
                break;
            }
        }


        return ticket;
    }

    @NotNull
    @Override
    public QRCodeLoginListener createQRCodeLoginListener(@NotNull Bot bot) {
        this.qq = bot.getId();
        this.bot = bot;
        return new QRCodeLoginListener() {

            @Override
            public void onStateChanged(@NotNull Bot bot, @NotNull QRCodeLoginListener.State state) {
                String msg = "[QRCodeLogin] ";
                switch (state){
                    case WAITING_FOR_SCAN:
                        log.info(msg + "等待扫描二维码中");
                        log.info(msg + "Waiting for scanning qrcode.");
                        break;
                    case WAITING_FOR_CONFIRM:
                        log.info(msg + "扫描完成，请在手机 QQ 确认登录");
                        log.info(msg + "Scan complete. Please confirm login.");
                        break;
                    case CANCELLED:
                        log.info(msg + "已取消登录，将会重新获取二维码");
                        log.info(msg + "Login cancelled, we will try to fetch qrcode again.");
                        break;
                    case TIMEOUT:
                        log.info(msg + "扫描超时，将会重新获取二维码");
                        log.info(msg + "Timeout scanning, we will try to fetch qrcode again.");
                        break;
                    case CONFIRMED:
                        log.info(msg + "扫描超时，将会重新获取二维码");
                        log.info(msg + "Login confirmed.");
                        break;
                    default:
                        log.info(msg + "已确认登录");
                        log.info(msg + "default state");
                }

                if (state == QRCodeLoginListener.State.CONFIRMED) {
                    type = 9;
                }
            }

            @Override
            public void onFetchQRCode(@NotNull Bot bot, @NotNull byte[] bytes) {
                log.info("[QRCodeLogin] 已获取登录二维码，请在手机 QQ 使用账号 ${} 扫码", bot.getId());
                log.info( "[QRCodeLogin] Fetched login qrcode, please scan via qq android with account {}.", bot.getId() );
                String qrcodePath = "qrcode/mirai-qrcode-"+ bot.getId() + "-" + System.currentTimeMillis() +".png";
                File qrcode = FileUtil.touch(qrcodePath);
                FileUtil.writeBytes(bytes, FileUtil.touch(qrcodePath));
                type = 5;
                url = qrcodePath;
                log.info("[QRCodeLogin] 将会显示二维码图片，若看不清图片，请查看文件 {}");
                log.info("[QRCodeLogin] Displaying qrcode image. If not clear, view file {}.",  qrcode.getAbsolutePath());
            }
        };
    }

    @Nullable
    @Override
    public Object onSolveDeviceVerification(@NotNull Bot bot, @NotNull DeviceVerificationRequests requests, @NotNull Continuation<? super DeviceVerificationResult> completion) {
        this.qq = bot.getId();
        this.bot = bot;
        if(requests.getSms() != null){
            solveSms(requests.getSms());
        }

        if(requests.getFallback() != null){
            solveFallback(requests.getFallback().getUrl());
            return requests.getFallback().solved();
        }
        log.error("User rejected SMS login while fallback login method not available.");

        return super.onSolveDeviceVerification(bot, requests, completion);
    }

    private void solveSms(DeviceVerificationRequests.SmsRequest request){
        String countryCode = request.getCountryCode();
        String phoneNumber = request.getPhoneNumber();
        if (countryCode != null && phoneNumber != null) {
            log.info("一条短信验证码将发送到你的手机 (+{}) {}. 运营商可能会收取正常短信费用, 是否继续? 确定，发送短信, 不确定会尝试其他验证方式.", countryCode, phoneNumber);
            log.info("A verification code will be send to your phone (+{}) {}, which may be charged normally, do you wish to continue? Type yes to continue, type others to cancel and try other methods.", countryCode, phoneNumber);
            this.type = 4;
        } else {
            log.info("一条短信验证码将发送到你的手机 (无法获取到手机号码). 运营商可能会收取正常短信费用.");
            log.info("A verification code will be send to your phone (failed to get phone number), " + "which may be charged normally by your carrier. ");
            this.type = 9;
        }
        Long currTime = System.currentTimeMillis();
        //最大等待检测1分钟，没有值返回
        for (;System.currentTimeMillis()-currTime <= 120000 && isSms == null;) {

        }

        Object[] data = new Object[1];
        //最大等待检测1分钟，没有值返回

        if(isSms != null){
            if(isSms){
                log.info("Attempting SMS verification.");

                request.requestSms(new Continuation<Object>() {
                    @Override
                    public void resumeWith(@NotNull Object o) {
                        data[0] = o;
                        //拿数据
                    }

                    @Override
                    public CoroutineContext getContext() {
                        return EmptyCoroutineContext.INSTANCE;
                    }
                });
                currTime = System.currentTimeMillis();
                //最大等待检测1分钟，没有值返回
                for (;System.currentTimeMillis()-currTime <= 120000;) {
                    if(StringUtils.isNotBlank(this.smsCode)){
                        log.info("Continuing with code '{}'.", this.smsCode);
                        request.solved(this.smsCode);
                        break;
                    }
                }
            }else{
                log.info("Cancelled.");
                this.type = 9;
            }
        }

    }

    private String solveFallback(String url){
        this.type = 3;
        this.url = url;
        log.info("[UnsafeLogin] 当前登录环境不安全，服务器要求账户认证。请在 QQ 浏览器打开 {} 并完成验证后输入任意字符。", url);
        log.info("[UnsafeLogin] Account verification required by the server. Please open {} in QQ browser and complete challenge, then type anything here to submit.", url);
        Long currTime = System.currentTimeMillis();
        //最大等待检测1分钟，没有值返回
        for (;System.currentTimeMillis()-currTime <= 60000;) {
            if(bot.isOnline()){
                break;
            }
        }
        return null;
    }
}
